/*
 * Copyright 2014, China UnionPay Co., Ltd. All right reserved.
 *
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF CHINA UNIONPAY CO., LTD. THE CONTENTS OF THIS FILE MAY NOT BE
 * DISCLOSED TO THIRD PARTIES, COPIED OR DUPLICATED IN ANY FORM, IN WHOLE OR IN PART, WITHOUT THE PRIOR WRITTEN
 * PERMISSION OF CHINA UNIONPAY CO., LTD.
 *
 */
package com.stylefeng.guns.core.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

import org.apache.ibatis.binding.MapperProxy;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;

public class AopTargetUtils {

	/**
	 * 获取 目标对象
	 * @param proxy 代理对象
	 * @author chenchen
	 * @return
	 * @throws Exception
	 * @date 2018/7/4 11:19
	 */
	public static Object getTarget(Object proxy) throws Exception {
		Object target = proxy;
		if (AopUtils.isAopProxy(proxy)) {
			if (AopUtils.isJdkDynamicProxy(proxy)) {
				target = getJdkDynamicProxyTargetObject(proxy);
			} else {
				target = getCglibProxyTargetObject(proxy);
			}
		}

		target = getUnProxyObject(target);
		// Mybatis去除mapperProxy,获得Mapper接口
		if (target instanceof MapperProxy) {
			for (Field field : MapperProxy.class.getDeclaredFields()) {
				if (field.getName().equals("mapperInterface")) {
					field.setAccessible(true);
					target = field.get(target);
					break;
				}
			}
		}
		return target;
	}

	/**
	 * 用于分离代理对象链，获得真实的target对象
	 * 
	 * @param target
	 * @return
	 */
	private static Object getUnProxyObject(Object target) {
		MetaObject metaObject = SystemMetaObject.forObject(target);
		// 分离代理对象链(由于目标类可能被多个拦截器拦截，从而形成多次代理，通过下面的两次循环
		// 可以分离出最原始的的目标类)
		while (metaObject.hasGetter("h")) {
			Object object = metaObject.getValue("h");
			metaObject = SystemMetaObject.forObject(object);
		}
		// 分离最后一个代理对象的目标类
		while (metaObject.hasGetter("target")) {
			Object object = metaObject.getValue("target");
			metaObject = SystemMetaObject.forObject(object);
		}
		target = metaObject.getOriginalObject();
		return target;
	}

	private static Object getCglibProxyTargetObject(Object proxy)
			throws Exception {
		Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
		h.setAccessible(true);
		Object dynamicAdvisedInterceptor = h.get(proxy);

		Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField(
				"advised");
		advised.setAccessible(true);

		Object target = ((AdvisedSupport) advised
				.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();

		return target;
	}

	private static Object getJdkDynamicProxyTargetObject(Object proxy)
			throws Exception {
		Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
		h.setAccessible(true);
		AopProxy aopProxy = (AopProxy) h.get(proxy);

		Field advised = aopProxy.getClass().getDeclaredField("advised");
		advised.setAccessible(true);

		Object target = ((AdvisedSupport) advised.get(aopProxy))
				.getTargetSource().getTarget();

		return target;
	}

	/**
	 * 切面获取切面方法上的注解。
	 * 
	 * @param point
	 * @param clzz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Annotation> T getAnnoatation(JoinPoint point, Class<T> clzz) {
		Annotation annotation = null;
		MethodSignature signature = (MethodSignature) point.getSignature();
		annotation = signature.getMethod().getAnnotation(clzz);

		if (annotation == null) {// 方法上没有获取到，去类上获取。类上没有去接口上获取。
			try {
				Object target = AopTargetUtils.getTarget(point.getTarget());
				annotation = target.getClass().getAnnotation(clzz);
				if (annotation == null && target instanceof Class<?>) {// 是接口的情况
					Class<?> tmp = (Class<?>) target;
					annotation = tmp.getAnnotation(clzz);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return (T) annotation;
	}
	
	/**
	 * 从Object中获取字段的值。级联的对象用.分隔开。暂时只支持普通的Java类，不支持获取Map，List等的值。</br>
	 * object={<br>
	 * 		field1:1, <br>
	 * 		field2:2<br>
	 * 		field3:{<br>
	 * 			field4:4<br>
	 * 		}<br>
	 * }<br>
	 * 
	 * getValue(field1) : 1;<br>
	 * getValue(field3.field4) : 4.<br>
	 * @param object
	 * @return
	 */
	public static Object getValue(Object object,String pattern){
		try {
			Object target = AopTargetUtils.getTarget(object);
			 MetaObject metaObject = SystemMetaObject.forObject(target);
			 return metaObject.getValue(pattern);
		} catch (Exception e) {
			e.printStackTrace();
			throw new IllegalArgumentException("获取对象" + object.getClass().toString() + "中的属性" + pattern + "错误" );
		}
	}
}